package com.yc.system.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yc.common.core.domain.entity.SysUser;
import com.yc.common.core.domain.model.EditPassBody;
import com.yc.common.core.domain.model.LoginUser;
import com.yc.common.exceptions.user.*;
import com.yc.common.utils.WebUtil;
import com.yc.common.utils.ip.AddressUtil;
import com.yc.framework.web.domain.TokenInfo;
import com.yc.framework.web.service.SysPasswordService;
import com.yc.system.mapper.SysUserMapper;
import com.yc.system.service.CaptchaService;
import com.yc.system.service.ISysPermissionService;
import com.yc.system.service.ISysUserRoleService;
import com.yc.system.service.ISysUserService;
import com.yc.common.utils.ip.IpUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.sql.SQLException;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

@Service
public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> implements ISysUserService {

    // 定义一个全局的 ReentrantLock（建议用 static final）
    private static final ReentrantLock PASSWORD_UPDATE_LOCK = new ReentrantLock();

    @Autowired
    private SysUserMapper sysUserMapper;

    @Autowired
    @Lazy
    private SysPasswordService passwordService;

    @Autowired
    private ISysPermissionService sysPermissionService;

    @Autowired
    private ISysUserRoleService sysUserRoleService;

    @Autowired
    private CaptchaService captchaService;

    /**
     * 更新用户登出时间
     */
    @Override
    public boolean updateLogoutTime(SysUser user) {
        // 仅更新登出时间字段
        SysUser updateUser = new SysUser();
        updateUser.setId(user.getId());
        updateUser.setLastLogoutTime(new Date());
        return this.updateById(updateUser);
    }

    /**
     * 更新用户登录时间和ip
     **/
    @Override
    public boolean updateUserProfile(SysUser user) {

        //获取当前请求ip地址
        String ipAddr = IpUtil.getLocalIpv4Address();

        //更新用户登录时间
        SysUser updateUser = new SysUser();
        updateUser.setId(user.getId());
        updateUser.setLastLoginIp(ipAddr);
        updateUser.setLastLoginTime(new Date());
        return this.updateById(updateUser);
    }

    /**
     * 获取用户信息-userId
     **/
    @Override
    public SysUser getSysUserById(Long userId) {
        // 使用手机号查询用户
        LambdaQueryWrapper<SysUser> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(SysUser::getId, userId);
        return sysUserMapper.selectOne(queryWrapper);
    }

    /**
     * 创建登录用户信息
     **/
    @Override
    public LoginUser createLoginUser(Long userId) {
        // 从数据库获取用户基本信息
        SysUser sysUser = getSysUserById(userId);
        if (sysUser == null) {
            return null;
        }

        // 获取用户权限
        ConcurrentHashMap<String, Set<String>> permissionsMap = sysPermissionService.selectPermissionsByUserId(userId);

        // 获取用户角色
        List<String> roles = sysUserRoleService.getRolesByUserId(userId);

        // 创建登录用户信息
        LoginUser loginUser = new LoginUser(userId, sysUser, roles, permissionsMap);

        // 设置登录时间和有效期
        loginUser.setLoginTime(System.currentTimeMillis());
        loginUser.setExpireTime(System.currentTimeMillis() + TimeUnit.DAYS.toMillis(3)); // 设置7天有效期

        // 设置IP地址和登录位置
        String ipaddr = WebUtil.getIpAddr();
        loginUser.setIpaddr(ipaddr);
        loginUser.setLoginLocation(AddressUtil.getRealAddressByIP(ipaddr));

        // 初始化令牌信息
        TokenInfo tokenInfo = new TokenInfo();
        loginUser.setTokenInfo(tokenInfo);

        return loginUser;
    }

    /**
     * 更新用户的密码
     **/
    @Override
    @Transactional(rollbackFor = SQLException.class)
    public void updateUserPassword(EditPassBody editPassBody) {
        String email = editPassBody.getEmail();
        String code = editPassBody.getCode();
        String captchaId = editPassBody.getCaptchaId();

        // 获取锁（可设置超时时间，避免死锁）
        boolean locked = false;
        try {
            locked = PASSWORD_UPDATE_LOCK.tryLock(3, TimeUnit.SECONDS); // 尝试获取锁，最多等待3秒
            if (!locked) {
                throw new UserException("系统繁忙，请稍后再试");
            }

            // 校验用户前置验证验证码
            captchaService.validateCaptchaForgetPassword(email, captchaId, code);

            // 获取用户信息
            SysUser user = validateUser(editPassBody);

            // 校验密码，确认与旧密码不一致
            passwordService.validatePassword(editPassBody, user.getPassword());

            // 加密新密码
            String newPassword = passwordService.encryptPassword(editPassBody.getPassword());

            LambdaUpdateWrapper<SysUser> lqw = new LambdaUpdateWrapper<>();
            lqw.eq(SysUser::getEmail, editPassBody.getEmail())
                    .set(SysUser::getPassword, newPassword)
                    .set(SysUser::getUpdateBy, user.getUserName())
                    .set(SysUser::getUpdateTime, LocalDateTime.now());

            // 更新用户信息
            sysUserMapper.update(null, lqw);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt(); // 恢复中断状态
            throw new UserException("操作被中断");
        } catch (UserException e) {
            throw e; // 直接抛出已知异常
        } catch (Exception e) {
            throw new UserException("密码更新失败：" + e.getMessage());
        } finally {
            if (locked) {
                PASSWORD_UPDATE_LOCK.unlock(); // 确保锁被释放
            }
        }
    }

    /**
     * 校验当前用户是否存在
     **/
    private SysUser validateUser(EditPassBody editPassBody) {
        String email = editPassBody.getEmail();

        LambdaQueryWrapper<SysUser> qw = Wrappers.<SysUser>lambdaQuery()
                .eq(SysUser::getEmail, email);
        SysUser user = sysUserMapper.selectOne(qw);

        if (user != null) {
            return user;
        } else {
            throw new UserNotExistsException("用户不存在，请检查邮箱");
        }
    }
}
